From: Keir Fraser Date: Wed, 19 May 2010 07:22:06 +0000 (+0100) Subject: VT-d: Fix ATS enabling for device assignment X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~12122 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https:/%22bookmarks://%22/%22http:/www.example.com/cgi/%22https:/%22bookmarks:/%22?a=commitdiff_plain;h=238deec7a647020bd882780ef18f1f0cd06494ee;p=xen.git VT-d: Fix ATS enabling for device assignment Currently, Xen only enables ATS in Xen booting. When an ATS capable device is assigned to guest, ATS is actually not enabled because FLR before assignment causes it to be disabled. Thus ATS cannot be used in guest. This patch enables ATS in domain_context_mapping. This ensures ATS is enabled in assignment because FLR is earlier than domain_context_mapping call. Therefore ATS can be used in guest. This patch also implements disable_ats_device to disable ATS when the device is deassigned from a domain. Signed-off-by: Weidong Han --- diff --git a/xen/drivers/passthrough/vtd/ia64/ats.c b/xen/drivers/passthrough/vtd/ia64/ats.c index ba8ecb2026..cfa97acc4e 100644 --- a/xen/drivers/passthrough/vtd/ia64/ats.c +++ b/xen/drivers/passthrough/vtd/ia64/ats.c @@ -47,6 +47,11 @@ int enable_ats_device(int seg, int bus, int devfn) return 0; } +int disable_ats_device(int seg, int bus, int devfn) +{ + return 0; +} + int dev_invalidate_iotlb(struct iommu *iommu, u16 did, u64 addr, unsigned int size_order, u64 type) { diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c index 71143cf4f7..e41c854798 100644 --- a/xen/drivers/passthrough/vtd/iommu.c +++ b/xen/drivers/passthrough/vtd/iommu.c @@ -1325,6 +1325,9 @@ static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn) dprintk(VTDPREFIX, "d%d:PCIe: map bdf = %x:%x.%x\n", domain->domain_id, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn); + if ( !ret && ats_device(0, bus, devfn) ) + enable_ats_device(0, bus, devfn); + break; case DEV_TYPE_PCI: @@ -1454,6 +1457,9 @@ static int domain_context_unmap(struct domain *domain, u8 bus, u8 devfn) dprintk(VTDPREFIX, "d%d:PCIe: unmap bdf = %x:%x.%x\n", domain->domain_id, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); ret = domain_context_unmap_one(domain, iommu, bus, devfn); + if ( !ret && ats_device(0, bus, devfn) ) + disable_ats_device(0, bus, devfn); + break; case DEV_TYPE_PCI: @@ -1772,8 +1778,6 @@ static void setup_dom0_devices(struct domain *d) list_add(&pdev->domain_list, &d->arch.pdev_list); domain_context_mapping(d, pdev->bus, pdev->devfn); pci_enable_acs(pdev); - if ( ats_device(0, pdev->bus, pdev->devfn) ) - enable_ats_device(0, pdev->bus, pdev->devfn); } } spin_unlock(&pcidevs_lock); diff --git a/xen/drivers/passthrough/vtd/x86/ats.c b/xen/drivers/passthrough/vtd/x86/ats.c index 3c32538516..e56c9d5ad4 100644 --- a/xen/drivers/passthrough/vtd/x86/ats.c +++ b/xen/drivers/passthrough/vtd/x86/ats.c @@ -92,6 +92,9 @@ int ats_device(int seg, int bus, int devfn) pdev = pci_get_pdev(bus, devfn); drhd = acpi_find_matched_drhd_unit(pdev); + if ( !drhd ) + return 0; + if ( !ecap_queued_inval(drhd->iommu->ecap) || !ecap_dev_iotlb(drhd->iommu->ecap) ) return 0; @@ -144,6 +147,9 @@ int enable_ats_device(int seg, int bus, int devfn) value = pci_conf_read16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pos + ATS_REG_CTL); + if ( value & ATS_ENABLE ) + return 0; + value |= ATS_ENABLE; pci_conf_write16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pos + ATS_REG_CTL, value); @@ -153,10 +159,50 @@ int enable_ats_device(int seg, int bus, int devfn) pdev->devfn = devfn; pdev->ats_queue_depth = queue_depth; list_add(&(pdev->list), &ats_devices); + if ( iommu_verbose ) + dprintk(XENLOG_INFO VTDPREFIX, "%x:%x.%x: ATS is enabled\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); return pos; } +int disable_ats_device(int seg, int bus, int devfn) +{ + struct list_head *pdev_list, *tmp; + struct pci_ats_dev *pdev; + u32 value; + int pos; + + pos = pci_find_ext_capability(seg, bus, devfn, PCI_EXT_CAP_ID_ATS); + if ( !pos ) + return 0; + + /* BUGBUG: add back seg when multi-seg platform support is enabled */ + value = pci_conf_read16(bus, PCI_SLOT(devfn), + PCI_FUNC(devfn), pos + ATS_REG_CTL); + value &= ~ATS_ENABLE; + pci_conf_write16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + pos + ATS_REG_CTL, value); + + list_for_each_safe( pdev_list, tmp, &ats_devices ) + { + pdev = list_entry(pdev_list, struct pci_ats_dev, list); + if ( pdev->bus == bus && pdev->devfn == devfn ) + { + list_del(&pdev->list); + xfree(pdev); + break; + } + } + + if ( iommu_verbose ) + dprintk(XENLOG_INFO VTDPREFIX, "%x:%x.%x: ATS is disabled\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + + return 0; +} + + static int device_in_domain(struct iommu *iommu, struct pci_ats_dev *pdev, u16 did) { struct root_entry *root_entry = NULL;